import { IWetAirPoint } from "../../types";

/** 湿空气状态点基类，根据干球温度与大气压力构造
 *
 */
export abstract class WetAirPointStateBase {
  constructor(dbTemp: number, press: number) {
    this.dbTemp = dbTemp;
    this.press = press;
  }

  /** 干球温度；单位：℃ */
  dbTemp: number;

  /** 大气压力；单位：Pa */
  press: number;

  protected abstract wetAirPoint(): IWetAirPoint;

  /** 饱和空气水蒸气分压力；单位：Pa；计算属性 */
  protected get saturationSubPre() {
    return this.calculateVaporSaturationPressure(this.dbTemp);
  }

  /** 饱和空气含湿量；单位：g/Kg；计算属性*/
  protected get saturationAbsHum() {
    return this.calculateHumidity(this.saturationSubPre, this.press);
  }

  /** 通过湿空气干球温度求解饱和水蒸气压力
   * @summary 计算湿空气水蒸气分压；ASHRAE 2017 Fundamentals Handbook 里就使用的这个公式;
   * @param{number} dbTemp 干球温度，单位：℃
   * @returns{number} 计算饱和湿空气水蒸气分压值，单位：Pa
   */
  protected calculateVaporSaturationPressure(dbTemp: number): number {
    const T = dbTemp + Constant.Tk;
    let tp: number = 0;
    if (dbTemp >= 0 && dbTemp < 200) {
      const c8 = -5.8002206 * Math.pow(10, 3);
      const c9 = 1.3914993 * Math.pow(10, 0);
      const c10 = -4.8640239 * Math.pow(10, -2);
      const c11 = 4.1764768 * Math.pow(10, -5);
      const c12 = -1.4452093 * Math.pow(10, -8);
      const c13 = 6.5459673 * Math.pow(10, 0);
      tp =
        c8 / T +
        c9 +
        c10 * T +
        c11 * Math.pow(T, 2) +
        c12 * Math.pow(T, 3) +
        c13 * Math.log(T);
    } else if (dbTemp < 0 && dbTemp > -200) {
      const c1 = -5.6745359 * Math.pow(10, 3);
      const c2 = 6.3925247 * Math.pow(10, 0);
      const c3 = -9.677843 * Math.pow(10, -3);
      const c4 = 6.2215701 * Math.pow(10, -7);
      const c5 = 2.0747825 * Math.pow(10, -9);
      const c6 = -9.484024 * Math.pow(10, -13);
      const c7 = 4.1635019 * Math.pow(10, 0);
      tp =
        c1 / T +
        c2 +
        c3 * T +
        c4 * Math.pow(T, 2) +
        c5 * Math.pow(T, 3) +
        c6 * Math.pow(T, 4) +
        c7 * Math.log(T);
    } else {
      throw new Error(Constant.ArgumentOutOfRange);
    }
    return Math.exp(tp);
  }

  /** 计算湿空气饱和含水量
   * @summary 计算湿空气含水量
   * @param preWater 水蒸气分压，单位：Pa
   * @param press 实测的大气压力，单位：Pa
   * @returns 每公斤干空气中水的质量，单位：g/kg
   * @remarks 0.621945-是水的相对分子质量与空气的相对分子质量的比值,18.015268是水的相对分子质量,28.966是空气的相对分子质量
   */
  protected calculateHumidity(
    preWater: number,
    press: number = Constant.PRESSURE
  ) {
    if (preWater >= press) throw new Error(Constant.ArgumentOutOfRange);
    return (621.945 * preWater) / (press - preWater);
  }

  /** 含湿量计算；
   * 4.186为水大于0℃时的平均比热容，单位为kJ/kgK.；
   * 2501为水在0℃时的汽化潜热,单位为kJ/kg；
   * 1.006为绝干空气的平均定压比热，单位为kJ/kgK；
   * 1.86为水的平均定压比热，单位为kJ/kgK。
   * @param dbTemp 干球温度,单位为℃
   * @param wbTemp 湿球温度,单位为℃
   * @param ws 用湿球温度算出的饱和含湿量，单位为kg/kg；
   * @returns 含湿量，单位为kg/kg
   */
  protected calcHumidity(dbTemp: number, wbTemp: number, ws: number) {
    if (dbTemp > 0) {
      return (
        ((2501 - 2.326 * wbTemp) * ws - 1.006 * (dbTemp - wbTemp)) /
        (2501 + 1.86 * dbTemp - 4.186 * wbTemp)
      );
    }
    return (
      ((2830 - 0.24 * wbTemp) * ws - 1.006 * (dbTemp - wbTemp)) /
      (2830 + 1.86 * dbTemp - 2.1 * wbTemp)
    );
  }

  /** 湿空气焓值计算公式；OK
   *  干空气的定压比热Cp·a=1.006 kJ/(kg·℃)，水蒸气的定压比热Cp·v=1.86 kJ/(kg·℃)。2501kJ/kg是每千克0℃的水变成0℃的水蒸气所需要的汽化潜热。湿空气的比焓一般是以1kg干空气作为基数进行计算的，伴随着1kg干空气的还有d kg水蒸气，如果取0℃的干空气和0℃水的比焓为零，则包含1kg干空气的湿空气的焓应是：
   * @param absHum 含湿量，单位：g/kg
   * @returns 焓值 单位：kj/kg
   */
  static computedEnth(dbTemp: number, absHum: number) {
    return 1.006 * dbTemp + (absHum * (2501 + 1.86 * dbTemp)) / 1000;
  }

  /** 相对湿度φ；OK
   * 水蒸气分压力pv有一个最大值，称为水蒸气饱和分压力，ps表示。它是一个与温度有关的数值，随温度的升高而增加。Pv与ps之间的关系反映了湿空气的饱和程度，二者之间的比值称为相对湿度φ
   * @param subPress 水蒸气分压值 Pa
   * @param saturationPressure 水蒸气饱和分压力  Pa
   * @returns 相对湿度φ
   */
  static computedRelHum(subPress: number, saturationPressure: number) {
    return subPress / saturationPressure;
  }

  /** 含湿量，单位为g/kg（a）；OK
   * 含湿量是指每千克干空气中所含的水蒸气量。
   * @param subPress 水蒸气分压，单位：Pa
   * @param press 大气压力，单位：Pa
   * @returns 含湿量，单位：g/Kg
   */
  static computedMoistureContent(subPress: number, press: number): number {
    return (621.945 * subPress) / (press - subPress);
  }

  /** 湿空气的露点温度计算公式；OK
   * 式中，td为湿空气露点温度，℃；α=lnpv；pv为水蒸气分压力，kPa；C14 = 6.54；C15 = 14.526；C16 = 0.7389；C17 = 0.09486；C18 = 0.4569。
   * @param dbTemp 干球温度，单位：℃
   * @param subPress 水蒸气分压力
   * @returns 露点温度
   */
  static computedDpTemp(dbTemp: number, subPress: number) {
    const c14 = 6.54;
    const c15 = 14.526;
    const c16 = 0.7389;
    const c17 = 0.09486;
    const c18 = 0.4569;
    const sp = subPress / 1000;
    const a = Math.log(sp);
    if (dbTemp >= 0) {
      return (
        c14 +
        c15 * a +
        c16 * Math.pow(a, 2) +
        c17 * Math.pow(a, 3) +
        c18 * Math.pow(sp, 0.1984)
      );
    }
    return 6.09 + 12.608 * a + 0.4959 * a * a;
  }

  /** 湿空气的密度ρ kg/m3 OK
   * 在标准条件下(p= 101.325 kPa, T= 293K) ，干空气与湿空气的密度相差较小，在工程上，取p= 1.2kg/m3 已足够精确。
   * @param dbTemp 干球温度 ℃
   * @param subPress 水蒸气分压
   * @param press 大气压力
   * @returns 湿空气的密度 kg/m3
   */
  static computedDensity(dbTemp: number, subPress: number, press: number) {
    const t = dbTemp + Constant.Tk;
    return (0.003484 * press - 0.00134 * subPress) / t;
  }

  /** 根据干球温度、湿球温度、饱和含湿量计算含湿量
   *
   * @param dbTemp 干球温度，℃
   * @param wbTemp 湿空气湿球温度，℃
   * @param moistureContent 湿空气等焓加湿到饱和状态时含湿量（简称饱和含湿量），g/kg(a)
   * @returns 含湿量,g/kg
   */
  static computedAbsHum(
    dbTemp: number,
    wbTemp: number,
    moistureContent: number
  ) {
    let hum: number;
    if (wbTemp >= 0) {
      hum =
        (((2501 - 2.326 * wbTemp) * moistureContent) / 1000 -
          1.006 * (dbTemp - wbTemp)) /
        (2501 + 1.86 * dbTemp - 4.186 * wbTemp);
    } else {
      hum =
        (((2830 - 0.24 * wbTemp) * moistureContent) / 1000 -
          1.006 * (dbTemp - wbTemp)) /
        (2830 + 1.86 * dbTemp - 2.1 * wbTemp);
    }
    return hum * 1000;
  }

  /** 由空气露点温度计算得到含湿量值
   *
   * @param dpTemp 空气露点温度，单位：℃
   * @returns 含湿量，单位：g/Kg
   */
  static getAbsHum(dpTemp: number) {
    return (
      3.703 +
      0.286 * dpTemp +
      9.164 * Math.pow(10, -3) * Math.pow(dpTemp, 2) +
      1.446 * Math.pow(10, -4) * Math.pow(dpTemp, 3) +
      1.741 * Math.pow(10, -6) * Math.pow(dpTemp, 4) +
      5.195 * Math.pow(10, -8) * Math.pow(dpTemp, 5)
    );
  }

  /** 标准大气的提出是为了提供一个标准参考，它只由海拔决定，而不考虑地理和气候因素的影响。
   * 例如，在海平面（海拔为0m），标准温度为15℃，标准大气压为101.325kPa。假设重力加速度为常数=9.806 65。
   * @param altitude 海拔高度，单位：m；范围：-5000~11000m
   * @returns 标准大气压，单位：Pa；
   */
  static computedStdPress(altitude: number) {
    if (altitude >= -5000 && altitude <= 11000) {
      const n = 1 - 2.5577 * Math.pow(10, -5) * altitude;
      return Constant.PRESSURE * Math.pow(n, 5.2559);
    } else {
      throw new Error(Constant.ArgumentOutOfRange);
    }
  }

  /** 根据海拔高度计算标准温度；
   *
   * @param altitude 海拔高度，单位：m；范围：-5000~11000m
   * @returns 温度，单位：℃
   */
  static computedStdTemp(altitude: number) {
    if (altitude >= -5000 && altitude <= 11000) {
      return 15 - 0.0065 * altitude;
    } else {
      throw new Error(Constant.ArgumentOutOfRange);
    }
  }

  /** 通过等焓值获取湿球温度
   * @summary 利用线性回归，最小二乘法，5次方线性方程；
   * @param airEnth 焓值，单位：Kj/Kg
   * @returns 湿球温度，单位：℃
   */
  protected calculateWtTempWithEnth(airEnth: number) {
    const a5 = -7.183855807169503 * Math.pow(10, -11);
    const a4 = -2.025237993935736 * Math.pow(10, -8);
    const a3 = 1.9991954981951447 * Math.pow(10, -5);
    const a2 = -0.004775110378950081;
    const a1 = 0.6632976772988047;
    const a0 = 267.3271876618238;
    const t =
      a5 * Math.pow(airEnth, 5) +
      a4 * Math.pow(airEnth, 4) +
      a3 * Math.pow(airEnth, 3) +
      a2 * Math.pow(airEnth, 2) +
      a1 * airEnth +
      a0;
    return t - 273.15;
  }

  /** 通过等湿线获取露点温度
   * @param absHum 含湿量，单位：g/Kg
   * @returns 露点温度，单位：℃
   */
  protected calculateDpTempWithAbsHum(absHum: number) {
    const a5 = 7.047017795714332 * Math.pow(10, -6);
    const a4 = -0.0008914210526869741;
    const a3 = 0.041735434515445674;
    const a2 = -0.9018095145508449;
    const a1 = 9.841344825197428;
    const a0 = 246.68816625409417;
    const t =
      a5 * Math.pow(absHum, 5) +
      a4 * Math.pow(absHum, 4) +
      a3 * Math.pow(absHum, 3) +
      a2 * Math.pow(absHum, 2) +
      a1 * absHum +
      a0;
    return t - 273.15;
  }

  /** 获取空气比容
   * @param dbTemp 干球温度，单位：℃
   * @param absHum 绝对含湿量，单位：g/kg
   * @param pre 大气压力，单位：Pa
   * @returns 空气比容是单位质量干空气空气所占的容积,specific volume(v),单位为m3/Kgda
   */
  protected GetSpecificVolume(
    dbTemp: number,
    absHum: number,
    pre: number
  ): number {
    return (
      (8.314472 * (273.15 + dbTemp) * (1 + (1.607858 * absHum) / 1000)) /
      ((28.966 * pre) / 1000)
    );
  }

  /** 获取湿空气密度
   * @param spV 空气比容,单位为m3/Kg
   * @param absHum 绝对含湿量，单位：g/kg
   * @returns 湿空气密度，单位：Kg/m3
   */
  protected calculateDensity(spV: number, absHum: number) {
    return (1 + absHum / 1000) / spV;
  }

  /** 根据海拔高度及干球温度计算大气压力
   *
   * @param altitude 海拔高度 m
   * @param dbTemp 空气干球温度，℃
   * @returns 大气压力
   */
  protected calculatePressure(altitude: number, dbTemp: number) {
    const num = altitude / (18400 * (1 + dbTemp / 273));
    return Constant.PRESSURE * Math.pow(10, num);
  }
}
